#include "Worker.h"
#include "Logger.h"

#include <QSqlError>
#include <QSqlRecord>
#include <QSqlQuery>
#include <QVariant>
#include <QFile>
#include <QDebug>

const char* header = "\x2"; //STX
const char* footer = "\x3"; //ETX
const char* separator = ";";

Worker::Worker(const QSettings& ini) 
: _watcher(new QFileSystemWatcher(QStringList() << ini.value("mdb", "").toString()))
, _table(ini.value("table", "").toString())
, _resultPath(ini.value("result", "").toString())
, _cachePath(ini.value("cache", "").toString())
, _cacheFile(_cachePath + "/" + _table)
, _dsn(ini.value("dsn", "").toString())
{
    _timer.setInterval(ini.value("timeout", "60000").toInt());
    logger.addn(QString("Timeout set to %1 msec").arg(_timer.interval()));
}

QByteArrayList Worker::GetData()
{
    QByteArrayList dataList;

    if (_db.isOpen() && _db.tables().contains(_table))
    {
        QSqlQuery query(QString("SELECT * FROM %1").arg(_table), _db);
        unsigned columns = query.record().count();
        while (query.next())
        {
            dataList << QByteArray();
            for (unsigned index = 0; index != columns; ++index)
                dataList.back() += query.value(index).toByteArray() + separator;
        }
    }
    else
        logger.addn(QString("Table \"%1\" doesn't exist").arg(_table));

    return dataList;   
}

QByteArrayList Worker::GetDiff(const QByteArrayList& data)
{
    QByteArrayList diff(data);
    unsigned index = 0;
    
    while(index != _cache.size() && !diff.isEmpty())
    {
        if (!diff.removeOne(_cache[index]))
            _missedLines.append(index);
        ++index;
    }

    return diff;
}

void Worker::Diff(const QString& path)
{
    SaveData(GetDiff(GetData()));
}

bool Worker::UpdateCache(const QByteArrayList& data)
{
    int oldSize = _cache.size();
    
    while(_missedLines.isEmpty())
        _cache.removeAt(_missedLines.takeLast());
    _cache << data;

    return oldSize != _cache.size();
}

void Worker::SaveData(const QByteArrayList& data)
{
	if (!data.isEmpty())
	{
        QFile file(_resultPath + "/result.dat");
		
		if (file.open(QIODevice::WriteOnly))
		{
            unsigned index = 0;
            while(index != data.size())
			    file.write(header + data[index++] + footer);

            if (UpdateCache(data))
                logger.addn(QString("<<%1. Cache updated.").arg(file.fileName()));
		}
		else
			logger.addn(QString("Can't open file \"%1\" for writing").arg(file.fileName()));
	}
}

bool Worker::MakePath(const QString& path)
{
    return QDir().exists(path) || QDir().mkpath(path) ? true : (logger.addn(QString("can't make path \"%1\"").arg(path)), false);
}

bool Worker::MakeConnection()
{
    _db = QSqlDatabase::addDatabase("QODBC");
    _db.setDatabaseName(_dsn.isEmpty() ? QString("DRIVER={Microsoft Access Driver (*.mdb)};FIL={MS Access};DBQ=%1").arg(_watcher->files().first()) : _dsn);

    return _db.open() ? (logger.addn(QString("\"%1\" opened").arg(_db.databaseName())), true) : (logger.addn(_db.lastError().text()), false);
}

bool Worker::LoadCache()
{
    QFile cacheFile(_cacheFile);

    if (cacheFile.exists() && cacheFile.open(QIODevice::ReadOnly))
    {
        while(!cacheFile.atEnd())
            _cache << cacheFile.readLine().trimmed();
    }

    return cacheFile.isOpen();
}

bool Worker::SaveCache()
{
    QFile cacheFile(_cacheFile);
    
    if (cacheFile.open(QIODevice::WriteOnly))
    {
        unsigned index = 0;
        while(index != _cache.size())
            cacheFile.write(_cache[index++] + "\n");
    }

    return cacheFile.isOpen();
}

bool Worker::CheckSource()
{
    return !_watcher->files().isEmpty() ? true : (logger.addn("Source file %1 doesn't exist"), false);
}

void Worker::run()
{	
	if (CheckSource() && MakePath(_resultPath) && MakePath(_cachePath) && MakeConnection())
	{
        logger.addn("Worker started");

        connect(_watcher.get(), SIGNAL(fileChanged(const QString&)), SLOT(Diff(const QString&)));
        connect(&_timer, SIGNAL(timeout()), SLOT(Diff()));

        if (!LoadCache())
            logger.addn(QString("Can't load \"%1\".").arg(_cacheFile));
		
        exec();

        if (!SaveCache())
            logger.addn(QString("Can't save \"%1\".").arg(_cacheFile));
	}
}
